package guet.imagan.progbuilder;

import guet.imagan.progbuilder.annotation.AnnotationTool;
import guet.imagan.util.ClassUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * 项目构建器工厂类
 */
public class BuilderFactory {

	long timeout = 1;

	/**
	 * 语言类型，分别为编译型，混合型、解释型
	 */
	public enum BuilderType {
		COMPILE, MIXED, INTERPRETE;
	}

	/**
	 * 实现Compiler,Interpreter的具体类的包名
	 */
	public static final String packageName = "guet.imagan.progbuilder.impl";
	public static final String[] compileType = {"c", "cpp", "go"};
	public static final String[] mixedType = {"java"};
	public static final String[] interpreteType = {"py"};
	/**
	 * 唯一的构建器工程实例
	 */
	private static BuilderFactory factory;

	static {
		try {
			factory = new BuilderFactory();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 当前电脑上所有具体的编译器对象
	 */
	List<Compiler> compilers;
	/**
	 * 当前电脑上所有具体的解释器对象
	 */
	List<Interpreter> interpreters;

	/**
	 * 构造器
	 */
	private BuilderFactory() throws Exception {

		//在类路径packageName下查找所有带限定名的类名字符串
		List<String> classNames = findAllClassNames();
		//更新编译器、解释器列表
		compilers = new ArrayList<>();
		interpreters = new ArrayList<>();
		updateCompilersAndInterpreters(classNames);
		if (compilers == null && interpreters == null) {
			String errMsg = String.format("no realization class for Compiler or Interpreter in:{%s}",
					packageName);
			throw new Exception(errMsg);
		}
	}

	/**
	 * 返回构造器工厂的唯一实例
	 *
	 * @return
	 * @throws Exception
	 */
	public static BuilderFactory getInstance() throws Exception {
		if (factory == null)
			factory = new BuilderFactory();
		return factory;
	}

	/**
	 * 根据类名字符串，更新compilers和interpreters属性
	 *
	 * @param classNames 带限定名的字符串
	 */
	private void updateCompilersAndInterpreters(List<String> classNames) {
		classNames.forEach(this::updateCompilersAndInterpreters);
	}

	/**
	 * 根据类名字符串更新编译器或解释器列表
	 *
	 * @param className 带限定名的类名字符串
	 */
	private void updateCompilersAndInterpreters(String className) {
		try {
			Compiler c = AnnotationTool.getCompiler(className);
			c.setTimeout(timeout);
			compilers.add(c);
			return;
		} catch (Exception e) {
		}
		try {
			Interpreter interpreter = AnnotationTool.getInterpreter(className);
			interpreter.setTimeout(timeout);
			interpreters.add(interpreter);
		} catch (Exception e) {
		}


	}

	/**
	 * 找出包“guet.imagan.progbuilder.impl”所有的类名（完全类名）
	 */
	private List<String> findAllClassNames() throws Exception {
		List<String>classNames= ClassUtil.findClassNames("guet.imagan.progbuilder.impl");
		return classNames;
	}

	/**
	 * 根据源程序文件的扩展名获取用于构建对应项目的构建器
	 * @param srcExt 源文件扩展名
	 * @return 构建器对象
	 * @throws Exception 获取失败时
 	 */
	public ProjectBuilder getBuilder(String srcExt) throws Exception {
		BuilderType builderType = findBuilderType(srcExt);
		switch (builderType) {
			case COMPILE:
				return getCompilerBuilder(srcExt);
			case MIXED:
				return getMixedBuilder(srcExt);
			case INTERPRETE:
				return getInterpreterBuilder(srcExt);
		}
		return null;
	}

	private ProjectBuilder getInterpreterBuilder(String srcExt) throws Exception {
		Interpreter p = findInterpreter(srcExt);
		if (p == null) {
			throw new Exception("找不到解释器");
		}
		return new ProjectBuilder(p);
	}

	private ProjectBuilder getMixedBuilder(String srcExt) throws Exception {
		Compiler c = findCompiler(srcExt);
		if (c == null) {
			throw new Exception("找不到编译器");
		}
		Interpreter s = findInterpreter(srcExt);
		if (s == null) {
			throw new Exception("找不到解释器");
		}
		return new ProjectBuilder(c, s);
	}

	private Interpreter findInterpreter(String srcExt) {
		for (Interpreter c : interpreters) {
			for (String s : c.getSrcExts()) {
				if (s.equals(srcExt))
					return c;
			}
		}
		return null;
	}

	private ProjectBuilder getCompilerBuilder(String srcExt) throws Exception {
		Compiler c = findCompiler(srcExt);
		if (c == null) {
			throw new Exception("未找到合适的编译器");
		}
		return new ProjectBuilder(c);
	}

	private Compiler findCompiler(String srcExt) {
		for (Compiler c : compilers) {
			for (String s : c.getSrcFileExts()) {
				if (s.equals(srcExt))
					return c;
			}
		}
		return null;
	}

	/**
	 * 找到适用于特定编程语言的构建器类型
	 * @param srcExt 源文件扩展名
	 * @return 构建器类型
	 */
	private static BuilderType findBuilderType(String srcExt) {
		for (String s : compileType) {
			if (s.equals(srcExt))
				return BuilderType.COMPILE;
		}
		for (String s : BuilderFactory.mixedType) {
			if (s.equals(srcExt)) {
				return BuilderType.MIXED;
			}
		}
		for (String s : BuilderFactory.interpreteType) {
			if (s.equals(srcExt)) {
				return BuilderType.INTERPRETE;
			}
		}
		return null;
	}
}
